package ph.com.gs3.formalistics.model.dao;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import ph.com.gs3.formalistics.model.tables.FormsTable;
import ph.com.gs3.formalistics.model.valueobjects.application.DocumentAction;
import ph.com.gs3.formalistics.model.valueobjects.application.Form;
import ph.com.gs3.formalistics.model.valueobjects.application.FormField;
import ph.com.gs3.formalistics.model.valueobjects.application.FormField.InvalidFormFieldException;
import ph.com.gs3.formalistics.model.valueobjects.application.FormStub;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;

public class FormsDAO extends SQLiteDataAccessObject {

	public static final String TAG = FormsDAO.class.getSimpleName();

	private String[] tableColumns;

	public FormsDAO(Context context) {
		super(context);

		tableColumns = new FormsTable().getTableColumns();
	}

	// ============================================================================
	// Update & Insert Methods

	public Form saveForm(Form form) throws SQLException, JSONException, InvalidFormFieldException {

		ContentValues cv = createCVFromForm(form);

		Form updatedOrSavedForm = null;
		if (isFormExisting(form.getWebId(), form.getServer())) {
			updatedOrSavedForm = updateForm(form);
		} else {
			int formDbId = (int) database.insertOrThrow(FormsTable.NAME, null, cv);
			updatedOrSavedForm = getForm(Integer.toString(formDbId));
		}

		return updatedOrSavedForm;

	}

	public Form updateForm(Form form) throws JSONException, InvalidFormFieldException {

		ContentValues cv = createCVFromForm(form);

		String webId = form.getWebId();
		String server = form.getServer();

		String whereClause = FormsTable.COL_WEB_ID + " = ? AND " + FormsTable.COL_SERVER + " = ?";
		String[] whereArgs = new String[] { webId, server };

		long affectedRows = database.update(FormsTable.NAME, cv, whereClause, whereArgs);

		if (affectedRows > 0) {
			return getForm(webId, server);
		} else {
			throw new SQLException("Failed to update form.");
		}

	}

	// ============================================================================
	// Query Methods

	public boolean isFormExisting(String webId, String server) {
		String whereClause = FormsTable.COL_WEB_ID + " = ? AND " + FormsTable.COL_SERVER + " = ?";
		String[] whereArgs = new String[] { webId, server };
		Cursor cursor = database.query(FormsTable.NAME, new String[] { FormsTable.COL_ID },
		        whereClause, whereArgs, null, null, null);

		int id = -1;
		if (cursor.moveToFirst() && cursor.getCount() > 0) {
			id = cursor.getInt(0);
		}

		cursor.close();

		return id > 0;

	}

	public int getFormCount(String server) {

		String whereClause = FormsTable.COL_SERVER + " = ?";
		String[] whereArgs = new String[] { server };

		Cursor cursor = database.query(FormsTable.NAME, new String[] { FormsTable.COL_ID },
		        whereClause, whereArgs, null, null, null);

		int formCount = cursor.getCount();

		cursor.close();
		return formCount;
	}

	public Form getForm(String databaseId) throws JSONException, InvalidFormFieldException {

		String whereClause = FormsTable.COL_ID + " = ?";
		String[] whereArgs = new String[] { databaseId };
		Cursor cursor = database.query(FormsTable.NAME, tableColumns, whereClause, whereArgs, null,
		        null, null);

		Form form = null;

		if (cursor.moveToFirst() && cursor.getCount() > 0) {
			form = cursorToForm(cursor);
		}

		cursor.close();

		return form;

	}

	public Form getForm(String webId, String server) throws JSONException,
	        InvalidFormFieldException {

		String whereClause = FormsTable.COL_WEB_ID + " = ? AND " + FormsTable.COL_SERVER + " = ?";
		String[] whereArgs = new String[] { webId, server };
		Cursor cursor = database.query(FormsTable.NAME, tableColumns, whereClause, whereArgs, null,
		        null, null);

		Form form = null;

		if (cursor.moveToFirst() && cursor.getCount() > 0) {
			form = cursorToForm(cursor);
		}

		cursor.close();

		return form;

	}

	public List<String> getAllFormNames() {

		List<String> forms = new ArrayList<String>();

		Cursor cursor = database.query(FormsTable.NAME, new String[] { FormsTable.COL_NAME }, null,
		        null, null, null, null);

		if (cursor.moveToFirst() && cursor.getCount() > 0) {

			while (!cursor.isAfterLast()) {
				forms.add(cursor.getString(0));
				cursor.moveToNext();
			}

		}

		cursor.close();
		return forms;

	}

	public List<FormStub> getAllFormIdNameStub() {

		List<FormStub> formIdNamePairList = new ArrayList<FormStub>();

		Cursor cursor = database.query(FormsTable.NAME, new String[] { FormsTable.COL_ID,
		        FormsTable.COL_WEB_ID, FormsTable.COL_NAME }, null, null, null, null, null);

		if (cursor.moveToFirst() && cursor.getCount() > 0) {

			while (!cursor.isAfterLast()) {
				formIdNamePairList.add(cursorToFormIdNameStub(cursor));
				cursor.moveToNext();
			}

		}

		cursor.close();
		return formIdNamePairList;

	}

	// ============================================================================
	// Parser Methods

	private FormStub cursorToFormIdNameStub(Cursor cursor) {

		//	@formatter:off
		//	initialize indices
		
		int idIndex		= cursor.getColumnIndexOrThrow(FormsTable.COL_ID);
		int webIdIndex 	= cursor.getColumnIndexOrThrow(FormsTable.COL_WEB_ID);
		int nameIndex 	= cursor.getColumnIndexOrThrow(FormsTable.COL_NAME);
		
		String dbId 	= cursor.getString(idIndex);
		String webId 	= cursor.getString(webIdIndex);
		String name 	= cursor.getString(nameIndex);
		
		//	@formatter:on

		FormStub stub = new FormStub();

		stub.dbId = dbId;
		stub.webId = webId;
		stub.name = name;

		return stub;

	}

	private Form cursorToForm(Cursor cursor) throws JSONException, InvalidFormFieldException {

		//	@formatter:off
		//	initialize indices
		
		int idIndex				= cursor.getColumnIndexOrThrow(FormsTable.COL_ID);
		int webIdIndex 			= cursor.getColumnIndexOrThrow(FormsTable.COL_WEB_ID);
		int serverIndex 		= cursor.getColumnIndexOrThrow(FormsTable.COL_SERVER);
		int companyIdIndex 		= cursor.getColumnIndexOrThrow(FormsTable.COL_COMPANY_ID);
		int nameIndex 			= cursor.getColumnIndexOrThrow(FormsTable.COL_NAME);		
		int workflowIdIndex 	= cursor.getColumnIndexOrThrow(FormsTable.COL_WORKFLOW_ID);
		int webTableNameIndex 	= cursor.getColumnIndexOrThrow(FormsTable.COL_WEB_TABLE_NAME);
		
		int fieldsIndex 				= cursor.getColumnIndexOrThrow(FormsTable.COL_FIELDS);
		int onCreateFieldsEnabledIndex	= cursor.getColumnIndexOrThrow(FormsTable.COL_ON_CREATE_FIELDS_ENABLED);
		int onCreateFieldsRequiredIndex	= cursor.getColumnIndexOrThrow(FormsTable.COL_ON_CREATE_FIELDS_REQUIRED);
		int onCreateFieldsHiddenIndex	= cursor.getColumnIndexOrThrow(FormsTable.COL_ON_CREATE_FIELDS_HIDDEN);
		
		int buttonsIndex 	= cursor.getColumnIndexOrThrow(FormsTable.COL_BUTTONS);
		int actionsIndex 	= cursor.getColumnIndexOrThrow(FormsTable.COL_ACTIONS);
		
		//	initialize values
		String dbId 			= cursor.getString(idIndex);
		String webId 			= cursor.getString(webIdIndex);
		String server 			= cursor.getString(serverIndex);
		String companyId 		= cursor.getString(companyIdIndex);
		String name 			= cursor.getString(nameIndex);
		String workflowId 		= cursor.getString(workflowIdIndex);
		String webTableName 	= cursor.getString(webTableNameIndex);
		
		String rawFields					= cursor.getString(fieldsIndex);
		String rawOnCreateFieldsEnabled 	= cursor.getString(onCreateFieldsEnabledIndex);
		String rawOnCreateFieldsRequired	= cursor.getString(onCreateFieldsRequiredIndex);
		String rawOnCreateFieldsHidden		= cursor.getString(onCreateFieldsHiddenIndex);		
		
		String rawButtons	= cursor.getString(buttonsIndex);
		String rawActions	= cursor.getString(actionsIndex);
		
		//	@formatter:on

		// generate the form fields
		JSONObject fieldsJSON = new JSONObject(rawFields);
		List<FormField> fields = Form.createFormFieldsFromJSONArray(fieldsJSON
		        .getJSONArray("fields"));

		// generate field properties
		JSONArray fieldsEnabledJSONArray = new JSONArray(rawOnCreateFieldsEnabled);
		List<String> onCreateFieldsEnabled = new ArrayList<String>();
		for (int i = 0; i < fieldsEnabledJSONArray.length(); i++) {
			onCreateFieldsEnabled.add(fieldsEnabledJSONArray.getString(i));
		}

		JSONArray fieldsRequiredJSONArray = new JSONArray(rawOnCreateFieldsRequired);
		List<String> onCreateFieldsRequired = new ArrayList<String>();
		for (int i = 0; i < fieldsRequiredJSONArray.length(); i++) {
			onCreateFieldsRequired.add(fieldsRequiredJSONArray.getString(i));
		}

		JSONArray fieldsHiddenJSONArray = new JSONArray(rawOnCreateFieldsHidden);
		List<String> onCreateFieldsHidden = new ArrayList<String>();
		for (int i = 0; i < fieldsHiddenJSONArray.length(); i++) {
			onCreateFieldsHidden.add(fieldsHiddenJSONArray.getString(i));
		}

		// generate the buttons and the actions
		List<String> buttons = new ArrayList<String>(Arrays.asList(rawButtons.split(",")));

		JSONArray actionsJSONArray = new JSONArray(rawActions);
		List<DocumentAction> onCreateActions = Form.createActionsFromJSONArray(actionsJSONArray);

		// create the actual form using the values and generated values

		Form form = new Form();

		form.setDbId(dbId);
		form.setWebId(webId);
		form.setServer(server);
		form.setCompanyId(companyId);
		form.setName(name);
		form.setWorkflowId(workflowId);
		form.setWebTableName(webTableName);

		form.setRawFormFields(rawFields);
		form.setRawOnCreateFieldsEnabled(rawOnCreateFieldsEnabled);
		form.setRawOnCreateFieldsRequired(rawOnCreateFieldsRequired);
		form.setRawOnCreateFieldsHidden(rawOnCreateFieldsHidden);

		form.setRawButtons(rawButtons);
		form.setRawActions(rawActions);

		form.setFields(fields);
		form.setOnCreateFieldsEnabled(onCreateFieldsEnabled);
		form.setOnCreateFieldsRequired(onCreateFieldsRequired);
		form.setOnCreateFieldsHidden(onCreateFieldsHidden);

		form.setButtons(buttons);
		form.setActions(onCreateActions);

		return form;
	}

	private ContentValues createCVFromForm(Form form) {

		ContentValues cv = new ContentValues();

		// the id is intentionally not set
		cv.put(FormsTable.COL_WEB_ID, form.getWebId());
		cv.put(FormsTable.COL_SERVER, form.getServer());
		cv.put(FormsTable.COL_COMPANY_ID, form.getCompanyId());
		cv.put(FormsTable.COL_NAME, form.getName());
		cv.put(FormsTable.COL_WORKFLOW_ID, form.getWorkflowId());
		cv.put(FormsTable.COL_WEB_TABLE_NAME, form.getWebTableName());

		cv.put(FormsTable.COL_FIELDS, form.getRawFormFields());
		cv.put(FormsTable.COL_ON_CREATE_FIELDS_ENABLED, form.getRawOnCreateFieldsEnabled());
		cv.put(FormsTable.COL_ON_CREATE_FIELDS_REQUIRED, form.getRawOnCreateFieldsRequired());
		cv.put(FormsTable.COL_ON_CREATE_FIELDS_HIDDEN, form.getRawOnCreateFieldsHidden());

		cv.put(FormsTable.COL_BUTTONS, form.getRawButtons());
		cv.put(FormsTable.COL_ACTIONS, form.getActions().toString());

		return cv;

	}
}
